package com.smallkingson.ylib.core.utils;

import android.annotation.SuppressLint;

import com.smallkingson.ylib.core.Yr;

import java.util.Iterator;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2016/2/16.
 */
public class YJSONs {


    /**
     * 这个方法比较完美 实现各种json格式的解析，注意的是 Bean字段的 属性要和json的key对应起来一样多，也可以更多
     *
     * @param json
     * @return
     */
    @SuppressLint("NewApi")
    public static <T> T JSONObjToBeanF1(JSONObject json, Class<T> beanClass) {

        T t = null;
        try {
            t = YBeanUtils.classNewInstance(beanClass);
            JSONArray names = json.names();
            for (int i = 0; i < names.length(); i++) {
                String name = (String) names.get(i);
                Object object = json.get(name);
                if (object instanceof JSONObject) {
                    Method method = beanClass.getMethod(
                            YBeanUtils.createMethodName(name),
                            beanClass.getDeclaredClasses()[0]);
                    // 如果
                    object = JSONObjToBeanF((JSONObject) object,
                            beanClass.getDeclaredClasses()[0]);
                    method.invoke(t, object);
                } else if (object instanceof JSONArray) {
                    Method method = beanClass.getMethod(
                            YBeanUtils.createMethodName(name), List.class);
                    object = JSONArrayToBeanListF((JSONArray) object,
                            YBeanUtils.getListGenericType(beanClass
                                    .getDeclaredField(name)));
                    method.invoke(t, object);
                } else if (object instanceof String) {
                    Method method = beanClass.getMethod(
                            YBeanUtils.createMethodName(name), String.class);
                    method.invoke(t, object);
                } else if (object instanceof Integer
                        || object.getClass() == int.class) {
                    Method method = beanClass.getMethod(
                            YBeanUtils.createMethodName(name), Integer.class);
                    method.invoke(t, object);
                } else if (object instanceof Double
                        || object.getClass() == double.class) {
                    Method method = beanClass.getMethod(
                            YBeanUtils.createMethodName(name), Double.class);
                    method.invoke(t, object);
                } else if (object instanceof Long
                        || object.getClass() == long.class) {
                    Method method = beanClass.getMethod(
                            YBeanUtils.createMethodName(name), Long.class);
                    method.invoke(t, object);
                } else if (object instanceof Boolean
                        || object.getClass() == boolean.class) {
                    Method method = beanClass.getMethod(
                            YBeanUtils.createMethodName(name), Boolean.class);
                    method.invoke(t, object);
                }
            }
        } catch (Exception e) {
            Yr.logError(e);
        }
        return t;
    }

    /**
     * @param json
     * @param beanClass
     * @return
     */
    @SuppressLint("NewApi")
    private static <T> T JSONObjToBeanF20150429(JSONObject json,
                                                final Class<T> beanClass) {

        try {
            final T t = YBeanUtils.classNewInstance(beanClass);
            JSONArray names = json.names();
            for (int i = 0; i < names.length(); i++) {

                final String name = (String) names.get(i);

                final Object object = json.get(name);

                YBeanUtils.setOnTypeListener(object, new YBeanUtils.OnTypeListener() {

                    @Override
                    public void onString(String value) {

                        String setMethod = YBeanUtils.createMethodName(name);
                        try {
                            Method method = beanClass.getMethod(setMethod,
                                    String.class);
                            method.invoke(t, value);
                        } catch (Exception e) {
                            Yr.logError(e);
                        }
                    }

                    @Override
                    public void onLong(long value) {
                        String setMethod = YBeanUtils.createMethodName(name);
                        try {
                            Method method = beanClass.getMethod(setMethod,
                                    long.class);
                            method.invoke(t, value);
                        } catch (Exception e) {
                            Yr.logError(e);
                        }
                    }

                    @Override
                    public void onInt(int value) {
                        String setMethod = YBeanUtils.createMethodName(name);
                        try {
                            Method method = beanClass.getMethod(setMethod,
                                    int.class);
                            method.invoke(t, value);
                        } catch (Exception e) {
                            // Yr.logError(e);
                            onDouble(value);
                        }
                    }

                    @Override
                    public void onFloat(float value) {
                        String setMethod = YBeanUtils.createMethodName(name);
                        try {
                            Method method = beanClass.getMethod(setMethod,
                                    float.class);
                            method.invoke(t, value);
                        } catch (Exception e) {
                            Yr.logError(e);
                        }
                    }

                    @Override
                    public void onBoolean(boolean value) {
                        String setMethod = YBeanUtils.createMethodName(name);
                        try {
                            Method method = beanClass.getMethod(setMethod,
                                    boolean.class);
                            method.invoke(t, value);
                        } catch (Exception e) {
                            Yr.logError(e);
                        }
                    }

                    @Override
                    public void onDouble(double value) {
                        String setMethod = YBeanUtils.createMethodName(name);
                        try {
                            Method method = beanClass.getMethod(setMethod,
                                    double.class);
                            method.invoke(t, value);
                        } catch (Exception e) {
                            Yr.logError(e);
                        }
                    }
                });

                // if (object instanceof JSONObject) {
                // Method method =
                // beanClass.getMethod(YBeanUtils.createMethodName(name),
                // beanClass.getDeclaredClasses()[0]);
                // // 如果
                // Object obj = JSONObjToBeanF((JSONObject) object,
                // beanClass.getDeclaredClasses()[0]);
                // method.invoke(t, obj);
                // } else if (object instanceof JSONArray) {
                // Method method =
                // beanClass.getMethod(YBeanUtils.createMethodName(name),
                // List.class);
                // Object obj = JSONArrayToBeanListF((JSONArray) object,
                // YBeanUtils.getListGenericType(beanClass.getDeclaredField(name)));
                // method.invoke(t, object);
                // }
            }
            System.out.println();
            // Yr.logError(t);
            return t;
        } catch (Exception e) {
            Yr.logError(e);
        }
        return null;
    }

    /**
     * @param json
     * @param beanClass
     * @return
     */
    @SuppressLint("NewApi")
    public static <T> T JSONObjToBeanF(JSONObject json, final Class<T> beanClass) {

        try {

            List<Method> setMethods = YBeanUtils.getSetMethods(beanClass);
            final T t = YBeanUtils.classNewInstance(beanClass);

            JSONArray names = json.names();

            for (int i = 0; i < names.length(); i++) {

                final String name = (String) names.get(i);
                String createMethodName = YBeanUtils.createMethodName(name);

                boolean hasMethod = false;

                for (Method m : setMethods) {

                    if (m.getName().equals(createMethodName)) {
                        hasMethod = true;
                        break;
                    }
                }

                if (!hasMethod) {
                    continue;
                }
                final Object object = json.get(name);

                String setMethod = YBeanUtils.createMethodName(name);
                Method method = null;
                try {
                    if (object instanceof JSONObject) {

                    } else if (object instanceof JSONArray) {

                    } else if (object instanceof String) {
                        method = beanClass.getMethod(setMethod, String.class);

                    } else if (object instanceof Integer) {
                        try {
                            method = beanClass.getMethod(setMethod,
                                    Integer.class);
                        } catch (Exception e) {
                            method = beanClass.getMethod(setMethod, int.class);
                        }
                    } else if (object.getClass() == int.class) {
                        try {
                            method = beanClass.getMethod(setMethod, int.class);
                        } catch (Exception e) {
                            method = beanClass.getMethod(setMethod,
                                    Integer.class);
                        }
                    } else if (object instanceof Double) {
                        try {
                            method = beanClass.getMethod(setMethod,
                                    Double.class);
                        } catch (Exception e) {
                            method = beanClass.getMethod(setMethod,
                                    double.class);
                        }
                    } else if (object.getClass() == double.class) {
                        try {
                            method = beanClass.getMethod(setMethod,
                                    double.class);
                        } catch (Exception e) {
                            method = beanClass.getMethod(setMethod,
                                    Double.class);
                        }
                    } else if (object instanceof Long) {
                        try {
                            method = beanClass.getMethod(setMethod, Long.class);
                        } catch (Exception e) {
                            method = beanClass.getMethod(setMethod, long.class);
                        }
                    } else if (object.getClass() == long.class) {
                        try {

                            method = beanClass.getMethod(setMethod, long.class);
                        } catch (Exception e) {
                            method = beanClass.getMethod(setMethod, Long.class);
                        }
                    } else if (object instanceof Boolean) {
                        try {
                            method = beanClass.getMethod(setMethod,
                                    Boolean.class);
                        } catch (Exception e) {
                            method = beanClass.getMethod(setMethod,
                                    boolean.class);
                        }
                    } else if (object.getClass() == boolean.class) {
                        try {
                            method = beanClass.getMethod(setMethod,
                                    boolean.class);
                        } catch (Exception e) {
                            method = beanClass.getMethod(setMethod,
                                    Boolean.class);
                        }
                    } else if (object instanceof Float) {
                        try {
                            method = beanClass
                                    .getMethod(setMethod, Float.class);
                        } catch (Exception e) {
                            method = beanClass
                                    .getMethod(setMethod, float.class);
                        }
                    } else if (object.getClass() == float.class) {
                        try {
                            method = beanClass
                                    .getMethod(setMethod, float.class);
                        } catch (Exception e) {
                            method = beanClass
                                    .getMethod(setMethod, Float.class);
                        }
                    }
                } catch (Exception e) {
                    Yr.d(e);
                    continue;
                }
                if (object == null) {
                    continue;
                }
                if (method == null) {
                    continue;
                }
                method.invoke(t, object);
            }
            // Yr.logError(t);
            return t;
        } catch (Exception e) {
            Yr.logError(e);
        }
        return null;
    }

    /**
     * @param json
     * @param beanClass
     * @return
     */
    @SuppressLint("NewApi")
    public static <T> T parse(JSONObject json, final Class<T> beanClass) {

        try {
            final T t = YBeanUtils.classNewInstance(beanClass);

            JSONArray names = json.names();

            for (int k = 0; k < names.length(); k++) {

                final String fieldName = (String) names.get(k);

                Method method = YBeanUtils.getSetMethodByFieldName(beanClass,
                        fieldName);

                Class<?> type = method.getParameterTypes()[0];

                if (type == String.class) {
                    method.invoke(t, json.getString(fieldName));
                } else if (type == int.class || type == Integer.class) {
                    method.invoke(t, json.getInt(fieldName));
                } else if (type == float.class || type == Float.class
                        || type == double.class || type == Double.class) {
                    method.invoke(t, json.getDouble(fieldName));
                } else if (type == boolean.class || type == Boolean.class) {
                    method.invoke(t, json.getBoolean(fieldName));
                } else if (type == long.class || type == Long.class) {
                    method.invoke(t, json.getLong(fieldName));
                }
            }
            return t;
        } catch (Exception e) {
            Yr.logError(e);
        }
        return null;
    }

    @SuppressLint("NewApi")
    public static <T> List<T> parse(JSONArray array, Class<T> beanClass) {

        T t = null;
        List<T> beanlist = null;
        try {
            beanlist = new ArrayList<T>();
            for (int i = 0; i < array.length(); i++) {
                JSONObject jsonObject = (JSONObject) array.get(i);
                t = parse(jsonObject, beanClass);
                Yr.w(t);
                beanlist.add(t);
            }
        } catch (Exception e) {
            Yr.logError(e);
        }
        return beanlist;
    }

    /**
     * @param json
     * @param beanClass
     * @return
     */
    @SuppressLint("NewApi")
    public static <T> T JSONObjToBeanF2(JSONObject json,
                                        final Class<T> beanClass) {

        try {
            final T t = YBeanUtils.classNewInstance(beanClass);
            JSONArray names = json.names();
            for (int i = 0; i < names.length(); i++) {

                final String name = (String) names.get(i);

                final Object object = json.get(name);

                YBeanUtils.setTypeWatcher(beanClass, name, new YBeanUtils.TypeWatcher() {

                    @Override
                    public void onString() {

                        Method method = null;

                        try {

                            method = beanClass.getMethod(
                                    YBeanUtils.createMethodName(name),
                                    String.class);

                            method.invoke(t, object);

                        } catch (Exception e) {
                            Yr.logError(e);
                        }

                    }

                    @Override
                    public void onInt() {

                        Method method = null;

                        try {

                            method = beanClass.getMethod(
                                    YBeanUtils.createMethodName(name),
                                    int.class);

                            if (object instanceof String) {

                                int i = Integer.parseInt((String) object);

                                method.invoke(t, i);
                            } else {

                                method.invoke(t, object);
                            }

                        } catch (Exception e) {
                            Yr.logError(e);
                        }

                    }

                    @Override
                    public void onFloat() {

                        Method method = null;

                        try {

                            method = beanClass.getMethod(
                                    YBeanUtils.createMethodName(name),
                                    float.class);

                            method.invoke(t, object);

                        } catch (Exception e) {
                            Yr.logError(e);
                        }

                    }

                    @Override
                    public void onLong() {

                        Method method = null;

                        try {

                            method = beanClass.getMethod(
                                    YBeanUtils.createMethodName(name),
                                    long.class);

                            method.invoke(t, object);

                        } catch (Exception e) {
                            Yr.logError(e);
                        }

                    }

                    @Override
                    public void onBoolean() {

                        Method method = null;

                        try {

                            method = beanClass.getMethod(
                                    YBeanUtils.createMethodName(name),
                                    boolean.class);

                            method.invoke(t, object);

                        } catch (Exception e) {
                            Yr.logError(e);
                        }

                    }

                    @Override
                    public void onIntObj() {

                        Method method = null;

                        try {

                            method = beanClass.getMethod(
                                    YBeanUtils.createMethodName(name),
                                    Integer.class);

                            if (object instanceof String) {

                                Integer i = Integer.parseInt((String) object);

                                method.invoke(t, i);
                            } else {

                                method.invoke(t, object);
                            }

                        } catch (Exception e) {
                            Yr.logError(e);
                        }

                    }

                    @Override
                    public void onLongObj() {

                        Method method = null;

                        try {

                            method = beanClass.getMethod(
                                    YBeanUtils.createMethodName(name),
                                    Long.class);

                            method.invoke(t, object);

                        } catch (Exception e) {
                            Yr.logError(e);
                        }

                    }

                    @Override
                    public void onFloatObj() {

                        Method method = null;

                        try {

                            method = beanClass.getMethod(
                                    YBeanUtils.createMethodName(name),
                                    Float.class);

                            method.invoke(t, object);

                        } catch (Exception e) {
                            Yr.logError(e);
                        }

                    }

                    @Override
                    public void onBooleanObj() {

                        Method method = null;

                        try {

                            method = beanClass.getMethod(
                                    YBeanUtils.createMethodName(name),
                                    Boolean.class);

                            method.invoke(t, object);

                        } catch (Exception e) {
                            Yr.logError(e);
                        }

                    }

                    @Override
                    public void onDoubleObj() {
                        Method method = null;

                        try {

                            method = beanClass.getMethod(
                                    YBeanUtils.createMethodName(name),
                                    Double.class);

                            method.invoke(t, object);

                        } catch (Exception e) {
                            Yr.logError(e);
                        }
                    }

                    @Override
                    public void onDouble() {
                        Method method = null;

                        try {

                            method = beanClass.getMethod(
                                    YBeanUtils.createMethodName(name),
                                    double.class);

                            method.invoke(t, object);

                        } catch (Exception e) {
                            Yr.logError(e);
                        }
                    }
                });
                if (object instanceof JSONObject) {
                    Method method = beanClass.getMethod(
                            YBeanUtils.createMethodName(name),
                            beanClass.getDeclaredClasses()[0]);
                    // 如果
                    Object obj = JSONObjToBeanF((JSONObject) object,
                            beanClass.getDeclaredClasses()[0]);
                    method.invoke(t, obj);
                } else if (object instanceof JSONArray) {
                    Method method = beanClass.getMethod(
                            YBeanUtils.createMethodName(name), List.class);
                    Object obj = JSONArrayToBeanListF((JSONArray) object,
                            YBeanUtils.getListGenericType(beanClass
                                    .getDeclaredField(name)));
                    method.invoke(t, object);
                }
            }
            return t;
        } catch (Exception e) {
            Yr.logError(e);
        }
        return null;
    }

    /**
     * 顾名思义
     *
     * @param array
     * @param beanClass 如果对象里面还有对象的话 要把对象里面的对象的Class都传进来
     * @return
     */
    @SuppressLint("NewApi")
    public static <T> List<T> JSONArrayToBeanListF(JSONArray array,
                                                   Class<T> beanClass) {

        T t = null;
        List<T> beanlist = null;
        try {
            beanlist = new ArrayList<T>();
            for (int i = 0; i < array.length(); i++) {
                JSONObject jsonObject = (JSONObject) array.get(i);
                t = JSONObjToBeanF(jsonObject, beanClass);
                beanlist.add(t);
            }
        } catch (Exception e) {
            Yr.logError(e);
        }
        return beanlist;
    }

    /**
     * 顾名思义
     *
     * @param array 如果对象里面还有对象的话 要把对象里面的对象的Class都传进来
     * @return
     */
    @SuppressLint("NewApi")
    public static <T> List<T> JSONArrayToBeanListF1(JSONArray array,
                                                    Class<T> beanClass) {

        T t = null;
        List<T> beanlist = null;
        try {
            beanlist = new ArrayList<T>();
            for (int i = 0; i < array.length(); i++) {
                JSONObject jsonObject = (JSONObject) array.get(i);
                t = JSONObjToBeanF1(jsonObject, beanClass);
                beanlist.add(t);
            }
        } catch (Exception e) {
            Yr.logError(e);
        }
        return beanlist;
    }

    /**
     * json格式的数据转成bean，这个方法允许bean的属性比json的key少
     *
     * @param json
     * @return
     */
    @SuppressLint("NewApi")
    public static <T> T JSONObjToBeanL(JSONObject json, Class<T> beanClass) {

        T t = null;
        try {
            t = YBeanUtils.classNewInstance(beanClass);
            for (Method setMethod : YBeanUtils.getSetMethods(beanClass)) {
                String fieldName = null;
                String methodName = setMethod.getName();
                if (methodName.contains("_")) {
                    // 为了避免数字为key的对象
                    fieldName = methodName
                            .substring(methodName.indexOf("_") + 1);
                } else {
                    fieldName = YBeanUtils.getFieldNameFromMethod(setMethod);
                }
                Class fieldType = setMethod.getParameterTypes()[0];
                if (fieldType == String.class) {
                    setMethod.invoke(t, json.getString(fieldName));
                } else if (fieldType == int.class || fieldType == Integer.class) {
                    setMethod.invoke(t, json.getInt(fieldName));
                } else if (fieldType == double.class
                        || fieldType == Double.class) {
                    setMethod.invoke(t, json.getDouble(fieldName));
                } else if (fieldType == long.class || fieldType == Long.class) {
                    setMethod.invoke(t, json.getLong(fieldName));
                } else if (fieldType == boolean.class
                        || fieldType == Boolean.class) {
                    setMethod.invoke(t, json.getBoolean(fieldName));
                } else if (fieldType == float.class || fieldType == Float.class) {
                    setMethod.invoke(t, (float) json.getDouble(fieldName));
                } else if (fieldType == List.class) {
                    JSONArray array = (JSONArray) json.get(fieldName);
                    List beanList = JSONArrayToBeanListL(array,
                            YBeanUtils.getListGenericType(beanClass
                                    .getDeclaredField(fieldName)));
                    setMethod.invoke(t, beanList);
                } else if (fieldType == beanClass.getDeclaredClasses()[0]) {
                    JSONObject object = (JSONObject) json.get(fieldName);
                    Object obj = JSONObjToBeanL(object, fieldType);
                    setMethod.invoke(t, obj);
                }
            }
        } catch (Exception e) {
            Yr.logError(e);
        }
        return t;
    }

    @SuppressLint("NewApi")
    public static <T> List<T> JSONArrayToBeanListL(JSONArray array,
                                                   Class<T> beanClass) {

        T t = null;
        List<T> beanlist = null;
        try {
            beanlist = new ArrayList<T>();
            for (int i = 0; i < array.length(); i++) {
                JSONObject jsonObject = (JSONObject) array.get(i);
                t = JSONObjToBeanL(jsonObject, beanClass);
                beanlist.add(t);
            }
        } catch (Exception e) {
            Yr.logError(e);
        }
        return beanlist;
    }

    /**
     * 用深度遍历搜索 递归搜索想要的JSONObject
     *
     * @param json
     * @param classes
     * @param objName
     * @return
     */
    public static <T> T PartJSONObjToBean(JSONObject json, Class[] classes,
                                          String objName) {

        T t = null;
        try {
            JSONArray names = json.names();
            for (int i = 0; i < names.length(); i++) {
                String name = (String) names.get(i);
                if (name.equals(objName)
                        && json.get(name) instanceof JSONObject) { // 找到了
                    // t=JSONObjToBeanL((JSONObject) json.get(name), classes);
                } else {
                    if (json.get(name) instanceof JSONObject) {
                        t = PartJSONObjToBean((JSONObject) json.get(name),
                                classes, objName);
                    } else {
                        return null;// 当前深度遍历没有搜索到
                    }
                }
            }
        } catch (Exception e) {
            Yr.logError(e);
        }
        return t;
    }

    public static JSONObject obj(JSONObject jsonObject, String name) {

        JSONObject obj = null;
        try {
            obj = jsonObject.getJSONObject(name);
        } catch (Exception e) {
            Yr.logError(e);
        }
        return obj;
    }
    public static void loadJSON(final JSONObject body, Object object,Class clazz) throws Exception {
        if (body == null || object == null) {
            throw new NullPointerException();
        }
        Iterator<String> keys = body.keys();
        while (keys.hasNext()) {
            String key= keys.next();
            Method method= YBeanUtils.getSetMethodByFieldName(clazz, key);
            if (method!=null) {
                method.invoke(object, body.get(key));
            }
        }
    }
    public static void loadJSON(final JSONObject body, Object object) throws Exception {
        loadJSON(body,object,object.getClass());
    }
    public static void loadJSON(final JSONObject body, Object object,Class clazz, String[] fields) throws Exception {
        if (body == null || object == null) {
            throw new NullPointerException();
        }
        for (final String field : fields) {
            Method setMethodByFieldName = YBeanUtils.getSetMethodByFieldName(clazz, field);
            final Object[] value = new Object[1];
            if (body.has(field)) {
                YBeanUtils.setOnTypeListener(object, setMethodByFieldName.getParameterTypes()[0], new YBeanUtils.OnTypeListener1<Object>() {
                    @Override
                    public void onInt(Object t) throws JSONException {
                        value[0] = body.getInt(field);
                    }
                    @Override
                    public void onFloat(Object t) throws JSONException {
                        value[0] = body.getDouble(field);
                    }

                    @Override
                    public void onString(Object t) throws JSONException {
                        value[0] = body.getString(field);
                    }

                    @Override
                    public void onBoolean(Object t) throws JSONException {
                        value[0] = body.getBoolean(field);
                    }

                    @Override
                    public void onLong(Object t) throws JSONException {
                        value[0] = body.getLong(field);
                    }

                    @Override
                    public void onDouble(Object o) throws JSONException {
                        value[0] = body.getDouble(field);
                    }
                });
                setMethodByFieldName.invoke(object, value[0]);
            }
        }
    }
    public static void loadJSON(final JSONObject body, Object object,String[] fields) throws Exception {
        loadJSON(body, object, object.getClass(),fields);
    }
    public static String toJSON(Object object) throws InvocationTargetException, IllegalAccessException, JSONException {
        Class<?> clazz = object.getClass();
        List<Method> getMethods = YBeanUtils.getGetMethods(clazz);
        JSONObject json = new JSONObject();
        for (Method method : getMethods) {
            String fieldNameFromMethod = YBeanUtils.getFieldNameFromMethod(method);
            Object value = method.invoke(object);
            json.put(fieldNameFromMethod, value);
        }
        return json.toString();
    }
    public static String toJSON(Object object,String[] fields) throws InvocationTargetException,
            IllegalAccessException,
            JSONException {
        Class<?> clazz = object.getClass();
        JSONObject json = new JSONObject();
        for (String field : fields) {
            Method getMethod = YBeanUtils.getGetMethodByFieldName(clazz,field);
            Object invoke = getMethod.invoke(object);
            json.put(field, invoke);
        }
        return json.toString();
    }
}
